package com.txw.javaCore.server.service;

import com.txw.javaCore.model.dto.PraiseRankDto;
import com.txw.javaCore.model.entity.Praise;
import com.txw.javaCore.model.mapper.PraiseMapper;
import com.txw.javaCore.model.request.PraiseRequest;
import com.txw.javaCore.server.service.redis.RedisPraise;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * @PROJECT_NAME: JavaCoreB2
 * @DESCRIPTION:
 * @Author: 涂玄武
 * @DATE: 2021/3/18 13:51
 */
@Service
@Slf4j
public class PraiseService {

    //定义点赞博客时加分布式锁对应的Key
    private static final String keyAddBlogLock = "RedisBlogPraiseAddLock";

    @Autowired
    private Environment env;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private PraiseMapper praiseMapper;

    @Autowired
    private RedisPraise redisPraise;

    /**
     * 点赞博客 无锁
     *
     * @param request
     */
    @Transactional(rollbackFor = Exception.class)
    public void addPraise(PraiseRequest request) throws Exception {
        //根据博客id和用户id查询当前用户的点赞记录
        Optional<Praise> optional = Optional.ofNullable(praiseMapper.seletctByBlogUserId(request.getBlogId(), request.getUserId()));
        //判断是否有点赞记录
        if (!optional.isPresent()) {
            //如果没有点赞记录，则创建博客点赞实体信息
            Praise praise = new Praise();
            //将前端提交的点赞请求相应的字段取值复制到新创建的点赞实体相应的字段取值中
            BeanUtils.copyProperties(request, praise);
            //点赞时间
            praise.setPraiseTime(new Date());
            //创建时间
            praise.setCreateTime(new Date());
            //设置点赞记录的状态，status=1为已点赞
            praise.setStatus(1);
            //插入用户点赞记录
            int total = praiseMapper.insertSelective(praise);
            //判断是否成功插入
            if (total > 0) {
                log.info("--点赞博客-{}-无锁-插入点赞记录成功--", request.getBlogId());
                redisPraise.cachePraiseBlog(request.getBlogId(), request.getUserId(), 1);

                //触发更新缓存中的排行榜
                this.cachePraiseTotal();
            }
        }

    }

    /**
     * 点赞微博，加分布式锁（针对同一用户高并发重复点赞的情况）
     *
     * @param request
     * @throws Exception
     */
    public void addPraiseLock(PraiseRequest request) throws Exception {
        //定义用于获取分布式锁的Redis的Key
        final String lockName = keyAddBlogLock + request.getBlogId() + request.getUserId();
        //获取一次性锁对象
        RLock rLock = redissonClient.getLock(lockName);
        rLock.lock(10L, TimeUnit.SECONDS);
        try {
            //根据博客id和用户id查询当前用户的点赞记录
            Optional<Praise> optional = Optional.ofNullable(praiseMapper.seletctByBlogUserId(request.getBlogId(), request.getUserId()));
            if (!optional.isPresent()) {
                Praise praise = new Praise();
                BeanUtils.copyProperties(request, praise);
                //点赞时间
                praise.setPraiseTime(new Date());
                //创建时间
                praise.setCreateTime(new Date());
                //设置点赞记录的状态，status=1为已点赞
                praise.setStatus(1);
                //插入用户点赞记录
                int total = praiseMapper.insertSelective(praise);
                //判断是否成功插入
                if (total > 0) {
                    log.info("--点赞博客-{}-加分布式锁-插入点赞记录成功--", request.getBlogId());
                    redisPraise.cachePraiseBlog(request.getBlogId(), request.getUserId(), 1);

                    //触发更新缓存中的排行榜
                    this.cachePraiseTotal();
                }
            }
        } catch (Exception e) {
            throw e;
        } finally {
            if (rLock != null) {
                rLock.unlock();
            }
        }

    }

    /**
     * 获取博客的点赞总数
     *
     * @param blogId
     * @return
     * @throws Exception
     */
    public Long getBlogPraiseTotal(final Integer blogId) throws Exception {
        //直接调用Redis服务封装好的“获取博客点赞总数”的方法
        return redisPraise.getCacheTotalBlog(blogId);
    }

    /**
     * 取消点赞博客
     *
     * @param request
     */
    @Transactional(rollbackFor = Exception.class)
    public void cancelPraise(PraiseRequest request) throws Exception {
        if (request.getUserId() != null && request.getBlogId() != null) {
            //当前用户取消点赞博客，更新相应的记录信息
            int result = praiseMapper.cancelPraiseBlog(request.getBlogId(), request.getUserId());
            //判断是否更新成功
            if (result > 0) {
                log.info("--取消点赞博客-{}-更新点赞记录成功--", request.getBlogId());
                redisPraise.cachePraiseBlog(request.getBlogId(), request.getUserId(), 0);

                //触发更新缓存中的排行榜
                this.cachePraiseTotal();
            }
        }
    }

    /**
     * 获取博客点赞排行榜（采用缓存）
     */
    public List<PraiseRankDto> getRankWithRedisson() throws Exception {
        return redisPraise.getBlogPraiseRank();
    }

    /**
     * 获取博客点赞排行榜（不采用缓存）
     */
    public List<PraiseRankDto> getRankWithNoRedisson() {
        return praiseMapper.getPraiseRank();
    }

    /**
     * 将当前博客id对应的点赞总数构造为实体，并添加进RList中，构造排行榜
     * 记录当前博客id和点赞总数，构造实体排行榜
     */
    private void cachePraiseTotal() {
        try {
            redisPraise.rankBlogPraise();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
